export class HTMLView {
  constructor({ container = document, ...options }) {
    this.container = typeof container === 'string' ? document.querySelector(container) : container
    this.selector = options.selector
    this.update = options.update || Function.prototype
    this.inView = options.inView || Function.prototype
    this.outView = options.outView || Function.prototype

    this.init()
    options.dynamic && this.dynamic()
  }

  init() {
    this.els = [...this.container.querySelectorAll(this.selector)]
    this.intersectionObserver = new IntersectionObserver(entries => {
      const inView = [],
        outView = []
      this.els.forEach(({ __data__ }) => __data__)
      for (const { isIntersecting, target } of entries) {
        const { __data__ } = target
        __data__.isIntersecting = isIntersecting
        ;(isIntersecting ? inView : outView).push(__data__)
      }
      inView.length && this.inView(inView)
      outView.length && this.outView(outView)
    })
    this.observe(this.els)
    this.update(this.els.map(el => el.__data__))
  }

  dynamic() {
    this.mutationObserver = new MutationObserver(() => {
      this.els = [...this.container.querySelectorAll(this.selector)]
      this.observe(this.els)
      this.update(this.els.map(el => el.__data__))
    })
    this.mutationObserver.observe(this.container, { childList: true, subtree: true })
  }

  setData(el) {
    if (el.__data__) return
    el.__data__ = { el, isIntersecting: false }
  }

  observe(els) {
    if (!Array.isArray(els)) {
      els = [els]
    }
    for (const el of els) {
      this.setData(el)
      this.intersectionObserver.observe(el)
    }
  }

  unobserve(els) {
    if (!Array.isArray(els)) {
      els = [els]
    }
    for (const el of els) {
      el.__data__ = null
      this.intersectionObserver.unobserve(el)
    }
  }

  disconnect() {
    this.intersectionObserver.disconnect()
    this.mutationObserver.disconnect()
  }
}
